package com.zxy.supplier_system.server.repository

import com.zxy.supplier_system.server.entity.*
import com.zxy.supplier_system.server.utils.SpecificationUtils
import com.zxy.supplier_system.server.utils.joinOnce
import jakarta.persistence.criteria.JoinType
import org.springframework.data.domain.Pageable
import org.springframework.data.domain.Slice
import org.springframework.data.jpa.domain.Specification
import org.springframework.data.jpa.repository.JpaRepository
import org.springframework.data.jpa.repository.JpaSpecificationExecutor
import org.springframework.data.jpa.repository.Query
import java.time.LocalDate
import java.time.OffsetDateTime

interface MarketOrderRepository : JpaRepository<MarketOrder, Int>, JpaSpecificationExecutor<MarketOrder> {


    @Query("select count(id) from MarketOrder where date(createdDateTime) = current_date() order by createdDateTime desc limit 1 ")
    fun countToday(): Int

    fun findByIdAndMarket_SystemService(id: Int, market_systemService: SystemService): MarketOrder?

    fun findAllByMarket_Id(market_id: Int, pageable: Pageable): Slice<MarketOrder>
    fun findByNumberAndMarket_SystemService(number: String, service: SystemService): MarketOrder?

    object Specifications {

        fun ifUCanSee(employee: Employee): Specification<MarketOrder> {
            if (employee.authorities.contains(Authority.卖场_订单_查询所有)) {
                return Specification.where(null)
            }
            return marketSaleManagerEqual(employee.id!!)
                .or(marketShoppingGuidesContains(employee.id!!))
        }

        fun systemServiceEqual(systemService: SystemService): Specification<MarketOrder> {
            return SystemServiceRepository.Specifications.systemServiceEquals(
                { root -> root.joinOnce("market") },
                systemService
            )
        }

        fun marketSaleManagerEqual(employeeId: Int): Specification<MarketOrder> {
            return Specification { root, _, criteriaBuilder ->
                MarketRepository.Specifications.saleManagerEqual(root.joinOnce("market"), criteriaBuilder, employeeId)
            }
        }

        fun marketShoppingGuidesContains(employeeId: Int): Specification<MarketOrder> {
            return Specification { root, query, criteriaBuilder ->
                MarketRepository.Specifications.shoppingGuidesContains(
                    root.joinOnce("market"),
                    criteriaBuilder,
                    query,
                    employeeId
                )
            }
        }

        fun startDateGreaterThanOrEqualTo(startDate: LocalDate?): Specification<MarketOrder> {
            return Specification { root, _, criteriaBuilder ->
                startDate?.let {
                    criteriaBuilder.greaterThanOrEqualTo(root.get("createdDateTime"), it)
                }
            }
        }

        fun endDateLessThanOrEqualTo(endDate: LocalDate?): Specification<MarketOrder> {
            return Specification { root, _, criteriaBuilder ->
                endDate?.let {
                    criteriaBuilder.lessThanOrEqualTo(root.get("createdDateTime"), it)
                }
            }
        }

        fun marketIn(marketIds: List<Int>): Specification<MarketOrder> {
            return SpecificationUtils.`in`({ root ->
                root.joinOnce<MarketOrder, Market>("market").get("id")
            }, marketIds)
        }

        fun employeeIn(employeeIds: List<Int>): Specification<MarketOrder> {
            return SpecificationUtils.`in`({ root ->
                root.joinOnce<MarketOrder, Employee>("employee").get("id")
            }, employeeIds)
        }

        fun statusEqual(status: MarketOrder.Status?): Specification<MarketOrder> {
            return Specification { root, query, criteriaBuilder ->
                if (status == null) {
                    null
                } else {
                    val submitDateTimePath = root.get<OffsetDateTime>("submitDateTime")
                    when (status) {
                        MarketOrder.Status.WAIT_SUBMIT -> {
                            criteriaBuilder.isNull(submitDateTimePath)
                        }

                        MarketOrder.Status.SUBMITTED -> {
                            criteriaBuilder.and(
                                criteriaBuilder.isNotNull(submitDateTimePath),
                                criteriaBuilder.isNull(
                                    root.joinOnce<MarketOrder, MarketDeliveryOrder>(
                                        "deliveryOrder",
                                        JoinType.LEFT
                                    )
                                )
                            )
                        }

                        else -> {
                            criteriaBuilder.and(
                                criteriaBuilder.isNotNull(
                                    root.joinOnce<MarketOrder, MarketDeliveryOrder>(
                                        "deliveryOrder",
                                        JoinType.LEFT
                                    )
                                )
                            )
                        }
                    }
                }
            }
        }

        fun idEqual(id: Int): Specification<MarketOrder> {
            return Specification { root,
                                   _,
                                   criteriaBuilder ->
                criteriaBuilder.equal(root.get<Int>("id"), id)
            }
        }

        fun submitDateTimeIsNull(): Specification<MarketOrder> {
            return Specification { root,
                                   _,
                                   criteriaBuilder ->
                criteriaBuilder.isNull(root.get<OffsetDateTime>("submitDateTime"))
            }
        }

        fun numberContains(number: String?): Specification<MarketOrder> {
            return SpecificationUtils.contains({
                it.get("number")
            }, number)
        }

        fun employeeEqual(id: Int?): Specification<MarketOrder> {
            if (id == null) {
                return SpecificationUtils.empty()
            }
            return Specification { root, _, cb ->
                cb.equal(root.joinOnce<MarketOrder, Employee>("employee").get<Int>("id"), id)
            }
        }
    }


}

